home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / ditroff / ditcap.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-07-11  |  7.3 KB  |  372 lines

  1. /*
  2.  * Copyright (c) 1983 Regents of the University of California.
  3.  * All rights reserved.  The Berkeley software License Agreement
  4.  * specifies the terms and conditions for redistribution.
  5.  */
  6.  
  7. #ifndef lint
  8. static char sccsid[] = "@(#)ditcap.c    5.1 (Berkeley) 6/6/85";
  9. #endif not lint
  10.  
  11. #define    BUFSIZ    1024
  12. #define MAXHOP    32    /* max number of tc= indirections */
  13.  
  14. #include <ctype.h>
  15. #include <stdio.h>
  16. /*
  17.  * termcap - routines for dealing with the terminal capability data base
  18.  *
  19.  * BUG:        Should use a "last" pointer in tbuf, so that searching
  20.  *        for capabilities alphabetically would not be a n**2/2
  21.  *        process when large numbers of capabilities are given.
  22.  * Note:    If we add a last pointer now we will screw up the
  23.  *        tc capability. We really should compile termcap.
  24.  *
  25.  * Essentially all the work here is scanning and decoding escapes
  26.  * in string capabilities.  We don't use stdio because the editor
  27.  * doesn't, and because living w/o it is not hard.
  28.  */
  29.  
  30. #define DITCAP_ON
  31.  
  32. #ifdef DITCAP_ON
  33. #define tgetent    dgetent
  34. #define tskip    dskip
  35. #define tgetstr    dgetstr
  36. #define tdecode ddecode
  37. #define tgetnum    dgetnum
  38. #define    tgetflag dgetflag
  39. #define tdecode ddecode
  40. #define tnchktc    dnchktc
  41. #define    tnamatch dnamatch
  42. #ifndef DITCAP
  43. #define DITCAP "/etc/ditcap"
  44. #endif
  45. /*  #define V6  */
  46. #endif
  47.  
  48. static    char *tbuf;
  49. static    int hopcount;        /* detect infinite loops in termcap, init 0 */
  50. char    *tskip();
  51. char    *tgetstr();
  52. char    *tdecode();
  53. char    *getenv();
  54.  
  55.  
  56. /*
  57.  * Get an entry for terminal name in buffer bp,
  58.  * from the termcap file.  Parse is very rudimentary;
  59.  * we just notice escaped newlines.
  60.  */
  61. tgetent(bp, name)
  62.     char *bp, *name;
  63. {
  64.     register char *cp;
  65.     register int c;
  66.     register int i = 0, cnt = 0;
  67.     char ibuf[BUFSIZ];
  68.     char *cp2, *cp3;
  69.     int tf;
  70.  
  71.     tbuf = bp;
  72.     tf = 0;
  73. #ifndef V6
  74.     cp = getenv("DITCAP");
  75.     /*
  76.      * TERMCAP can have one of two things in it. It can be the
  77.      * name of a file to use instead of /etc/termcap. In this
  78.      * case it better start with a "/". Or it can be an entry to
  79.      * use so we don't have to read the file. In this case it
  80.      * has to already have the newlines crunched out.
  81.      */
  82.     if (cp && *cp) {
  83.         if (*cp!='/') {
  84.             cp2 = getenv("PRINTER");
  85.             if (cp3=getenv("TYPESETTER")) cp2 = cp3;
  86.             if (cp2==(char *) 0 || strcmp(name,cp2)==0) {
  87.                 strcpy(bp,cp);
  88.                 return(tnchktc());
  89.             } else {
  90.                 tf = open(DITCAP, 0);
  91.             }
  92.         } else
  93.             tf = open(cp, 0);
  94.     }
  95.     if (tf==0)
  96.         tf = open(DITCAP, 0);
  97. #else
  98.     /* if one wants to let version 6 change file */
  99.     cp = DITCAP;
  100.     if (cp2=getenv("DITCAP")) cp=cp2;
  101.     tf = open(cp, 0);
  102.     /* if we assume version 6 can't change the file 
  103.     tf = open(DITCAP, 0);  */
  104. #endif
  105.     if (tf < 0)
  106.         return (-1);
  107.     for (;;) {
  108.         cp = bp;
  109.         for (;;) {
  110.             if (i == cnt) {
  111.                 cnt = read(tf, ibuf, BUFSIZ);
  112.                 if (cnt <= 0) {
  113.                     close(tf);
  114.                     return (0);
  115.                 }
  116.                 i = 0;
  117.             }
  118.             c = ibuf[i++];
  119.             if (c == '\n') {
  120.                 if (cp > bp && cp[-1] == '\\'){
  121.                     cp--;
  122.                     continue;
  123.                 }
  124.                 break;
  125.             }
  126.             if (cp >= bp+BUFSIZ) {
  127.                 write(2,"Termcap entry too long\n", 23);
  128.                 break;
  129.             } else
  130.                 *cp++ = c;
  131.         }
  132.         *cp = 0;
  133.  
  134.         /*
  135.          * The real work for the match.
  136.          */
  137.         if (tnamatch(name)) {
  138.             close(tf);
  139.             return(tnchktc());
  140.         }
  141.     }
  142. }
  143.  
  144. /*
  145.  * tnchktc: check the last entry, see if it's tc=xxx. If so,
  146.  * recursively find xxx and append that entry (minus the names)
  147.  * to take the place of the tc=xxx entry. This allows termcap
  148.  * entries to say "like an HP2621 but doesn't turn on the labels".
  149.  * Note that this works because of the left to right scan.
  150.  */
  151. tnchktc()
  152. {
  153.     register char *p, *q;
  154.     char *tcname;        /* name of similar terminal */
  155.     char tcbuf[BUFSIZ];
  156.     char *holdtbuf = tbuf;
  157.     int l;
  158.  
  159.     p = tbuf + strlen(tbuf) - 2;    /* before the last colon */
  160.     while (*--p != ':')
  161.         if (p<tbuf) {
  162.             write(2, "Bad termcap entry\n", 18);
  163.             return (0);
  164.         }
  165.     p++;
  166.     /* p now points to beginning of last field */
  167.     if (p[0] != 't' || p[1] != 'c')
  168.         return(1);
  169.     tcname = p + 3;
  170.     q = tcname;
  171.     while (q && *q != ':')
  172.         q++;
  173.     *q = 0;
  174.     if (++hopcount > MAXHOP) {
  175.         write(2, "Infinite tc= loop\n", 18);
  176.         return (0);
  177.     }
  178.     if (tgetent(tcbuf, tcname) != 1)
  179.         return(0);
  180.     for (q=tcbuf; *q != ':'; q++)
  181.         ;
  182.     l = p - holdtbuf + strlen(q);
  183.     if (l > BUFSIZ) {
  184.         write(2, "Termcap entry too long\n", 23);
  185.         q[BUFSIZ - (p-tbuf)] = 0;
  186.     }
  187.     strcpy(p, q+1);
  188.     tbuf = holdtbuf;
  189.     return(1);
  190. }
  191.  
  192. /*
  193.  * Tnamatch deals with name matching.  The first field of the termcap
  194.  * entry is a sequence of names separated by |'s, so we compare
  195.  * against each such name.  The normal : terminator after the last
  196.  * name (before the first field) stops us.
  197.  */
  198. tnamatch(np)
  199.     char *np;
  200. {
  201.     register char *Np, *Bp;
  202.  
  203.     Bp = tbuf;
  204.     if (*Bp == '#')
  205.         return(0);
  206.     for (;;) {
  207.         for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
  208.             continue;
  209.         if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
  210.             return (1);
  211.         while (*Bp && *Bp != ':' && *Bp != '|')
  212.             Bp++;
  213.         if (*Bp == 0 || *Bp == ':')
  214.             return (0);
  215.         Bp++;
  216.     }
  217. }
  218.  
  219. /*
  220.  * Skip to the next field.  Notice that this is very dumb, not
  221.  * knowing about \: escapes or any such.  If necessary, :'s can be put
  222.  * into the termcap file in octal.
  223.  */
  224. static char *
  225. tskip(bp)
  226.     register char *bp;
  227. {
  228.  
  229.     while (*bp && *bp != ':')
  230.         bp++;
  231.     if (*bp == ':')
  232.         bp++;
  233.     return (bp);
  234. }
  235.  
  236. /*
  237.  * Return the (numeric) option id.
  238.  * Numeric options look like
  239.  *    li#80
  240.  * i.e. the option string is separated from the numeric value by
  241.  * a # character.  If the option is not found we return -1.
  242.  * Note that we handle octal numbers beginning with 0.
  243.  */
  244. tgetnum(id)
  245.     char *id;
  246. {
  247.     register int i, base;
  248.     register char *bp = tbuf;
  249.  
  250.     for (;;) {
  251.         bp = tskip(bp);
  252.         if (*bp == 0)
  253.             return (-1);
  254.         if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
  255.             continue;
  256.         if (*bp == '@')
  257.             return(-1);
  258.         if (*bp != '#')
  259.             continue;
  260.         bp++;
  261.         base = 10;
  262.         if (*bp == '0')
  263.             base = 8;
  264.         i = 0;
  265.         while (isdigit(*bp))
  266.             i *= base, i += *bp++ - '0';
  267.         return (i);
  268.     }
  269. }
  270.  
  271. /*
  272.  * Handle a flag option.
  273.  * Flag options are given "naked", i.e. followed by a : or the end
  274.  * of the buffer.  Return 1 if we find the option, or 0 if it is
  275.  * not given.
  276.  */
  277. tgetflag(id)
  278.     char *id;
  279. {
  280.     register char *bp = tbuf;
  281.  
  282.     for (;;) {
  283.         bp = tskip(bp);
  284.         if (!*bp)
  285.             return (0);
  286.         if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
  287.             if (!*bp || *bp == ':')
  288.                 return (1);
  289.             else if (*bp == '@')
  290.                 return(0);
  291.         }
  292.     }
  293. }
  294.  
  295. /*
  296.  * Get a string valued option.
  297.  * These are given as
  298.  *    cl=^Z
  299.  * Much decoding is done on the strings, and the strings are
  300.  * placed in area, which is a ref parameter which is updated.
  301.  * No checking on area overflow.
  302.  */
  303. char *
  304. tgetstr(id, area)
  305.     char *id, **area;
  306. {
  307.     register char *bp = tbuf;
  308.  
  309.     for (;;) {
  310.         bp = tskip(bp);
  311.         if (!*bp)
  312.             return (0);
  313.         if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
  314.             continue;
  315.         if (*bp == '@')
  316.             return(0);
  317.         if (*bp != '=')
  318.             continue;
  319.         bp++;
  320.         return (tdecode(bp, area));
  321.     }
  322. }
  323.  
  324. /*
  325.  * Tdecode does the grung work to decode the
  326.  * string capability escapes.
  327.  */
  328. static char *
  329. tdecode(str, area)
  330.     register char *str;
  331.     char **area;
  332. {
  333.     register char *cp;
  334.     register int c;
  335.     register char *dp;
  336.     int i;
  337.  
  338.     cp = *area;
  339.     while ((c = *str++) && c != ':') {
  340.         switch (c) {
  341.  
  342.         case '^':
  343.             c = *str++ & 037;
  344.             break;
  345.  
  346.         case '\\':
  347.             dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
  348.             c = *str++;
  349. nextc:
  350.             if (*dp++ == c) {
  351.                 c = *dp++;
  352.                 break;
  353.             }
  354.             dp++;
  355.             if (*dp)
  356.                 goto nextc;
  357.             if (isdigit(c)) {
  358.                 c -= '0', i = 2;
  359.                 do
  360.                     c <<= 3, c |= *str++ - '0';
  361.                 while (--i && isdigit(*str));
  362.             }
  363.             break;
  364.         }
  365.         *cp++ = c;
  366.     }
  367.     *cp++ = 0;
  368.     str = *area;
  369.     *area = cp;
  370.     return (str);
  371. }
  372.